{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import BertTokenizer, BertModel,AutoModel,BertTokenizerFast\n",
    "import torch\n",
    "from KModel import BertLSTM\n",
    "from torch.utils.data import Dataset,DataLoader\n",
    "import os \n",
    "from dataset import keywordsDataset,hufu_dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "pretrained_model = 'bert-base-chinese'\n",
    "tokenizer = BertTokenizerFast.from_pretrained(pretrained_model)\n",
    "\n",
    "transformer = AutoModel.from_pretrained('bert-base-chinese')\n",
    "num_cls = 5 \n",
    "freeze = True\n",
    "num_hiddens,num_layers = 125,2\n",
    "model = BertLSTM(transformer, num_hiddens,num_layers, freeze,num_cls)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 加载数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class keywordsDataset(Dataset):\n",
    "    def __init__(self,data_dir,tokenizer,flag='train',max_length=512) -> None:\n",
    "        '''\n",
    "        data_dir: 存放数据的目录地址\n",
    "        tokenizer: 分词器\n",
    "        flag: 可选，'train','test'\n",
    "        max_length: 每句话最长限度\n",
    "        '''\n",
    "        assert flag in ['train','test'],\"falg参数设置错误！\"\n",
    "\n",
    "        self.tokenizer = tokenizer\n",
    "        self.datas = []\n",
    "        self.max_length = max_length\n",
    "        self.flag = flag \n",
    "        self.text = []\n",
    "        self.keywords = []\n",
    "        self.lable = []\n",
    "        # 加载数据集\n",
    "        self.data_dir = os.path.join(data_dir,flag+'.txt')\n",
    "\n",
    "        with open(self.data_dir,'r',encoding='utf-8-sig') as fp:\n",
    "            data = fp.readlines()\n",
    "            for i in range(0,len(data),3):\n",
    "                self.text.append(data[i].strip())\n",
    "                self.keywords.append(data[i+1].strip())\n",
    "                self.lable.append(int(data[i+2].strip()))\n",
    "        pass\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.text)\n",
    "    \n",
    "    def __getitem__(self, idx):\n",
    "        # 使用tokenizer对文本进行编码，并设置max_length和padding参数以执行填充操作\n",
    "        encoded_input = self.tokenizer.encode_plus(\n",
    "            self.text[idx], max_length=self.max_length, padding=\"max_length\", truncation=True, return_tensors=\"pt\"\n",
    "        )\n",
    "\n",
    "        input_ids = encoded_input['input_ids'].squeeze(0)\n",
    "        kwidx = torch.tensor(tokenizer.convert_tokens_to_ids(tokenizer.tokenize(self.keywords[idx])))   # 关键词的编码列表\n",
    "\n",
    "        # 创建一个掩码，初始化为全 False\n",
    "        mask = torch.zeros(len(input_ids) - len(kwidx) + 1, dtype=torch.bool)\n",
    "        # 遍历 input_ids 中所有可能的起始位置\n",
    "        for i in range(len(input_ids) - len(kwidx) + 1):\n",
    "            # 提取从位置 i 开始的子序列\n",
    "            sub_tensor = input_ids[i:i + len(kwidx)]\n",
    "            # 检查子序列是否与 b 相等\n",
    "            if torch.equal(sub_tensor, kwidx):\n",
    "                mask[i] = True\n",
    "\n",
    "        # 如果需要将掩码扩展到与 input_ids 同样的长度\n",
    "        full_mask = torch.zeros(len(input_ids), dtype=torch.bool)\n",
    "        for i in range(len(mask)):\n",
    "            if mask[i]:\n",
    "                full_mask[i:i + len(kwidx)] = True\n",
    "                # 返回编码结果,掩码和对应的标签\n",
    "\n",
    "        aliged_labels = [-100 if i is None else int(full_mask[i]) for i in encoded_input.word_ids()]\n",
    "        keywordLable = torch.tensor(aliged_labels)\n",
    "        return input_ids,(encoded_input['attention_mask']==1).squeeze(0),keywordLable,self.lable[0]  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "fold_dir = 'data'\n",
    "train_data = keywordsDataset(fold_dir,tokenizer)\n",
    "val_data = keywordsDataset(fold_dir,tokenizer,'test')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 护肤品评价数据集"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data = hufu_dataset(pretrained_model,r'data\\new_data121190\\train_text.txt',r'data\\new_data121190\\vocab.txt',max_length=512)\n",
    "val_data = hufu_dataset(pretrained_model,r'data\\new_data121190\\dev_text.txt',r'data\\new_data121190\\vocab.txt','dev',max_length=512)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_loader = DataLoader(train_data,batch_size=5,shuffle=True)\n",
    "val_loader = DataLoader(val_data,batch_size=25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<All keys matched successfully>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.load_state_dict(torch.load('ac93.pth'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 训练模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "100%|██████████| 180/180 [01:04<00:00,  2.79it/s]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/10\n",
      "Train Loss: 0.0001 Acc: 0.8472 Recall: 0.9653799384850147\n",
      "Val Loss: 0.0018 Acc: 0.4112 Recall: 1.3004347126066655\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 19%|█▉        | 34/180 [00:05<00:21,  6.64it/s]\n"
     ]
    },
    {
     "ename": "KeyboardInterrupt",
     "evalue": "",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[8], line 35\u001b[0m\n\u001b[0;32m     33\u001b[0m loss \u001b[38;5;241m=\u001b[39m loss_func(batch_out, true_labels)\n\u001b[0;32m     34\u001b[0m loss\u001b[38;5;241m.\u001b[39mbackward()\n\u001b[1;32m---> 35\u001b[0m optimizer\u001b[38;5;241m.\u001b[39mstep()\n\u001b[0;32m     37\u001b[0m running_loss \u001b[38;5;241m+\u001b[39m\u001b[38;5;241m=\u001b[39m loss\u001b[38;5;241m.\u001b[39mitem()\n\u001b[0;32m     38\u001b[0m _, predictions \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39mmax(batch_out, \u001b[38;5;241m1\u001b[39m)\n",
      "File \u001b[1;32me:\\anaconda\\envs\\huggingface\\Lib\\site-packages\\torch\\optim\\optimizer.py:391\u001b[0m, in \u001b[0;36mOptimizer.profile_hook_step.<locals>.wrapper\u001b[1;34m(*args, **kwargs)\u001b[0m\n\u001b[0;32m    386\u001b[0m         \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    387\u001b[0m             \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[0;32m    388\u001b[0m                 \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m must return None or a tuple of (new_args, new_kwargs), but got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresult\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m    389\u001b[0m             )\n\u001b[1;32m--> 391\u001b[0m out \u001b[38;5;241m=\u001b[39m func(\u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m    392\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_optimizer_step_code()\n\u001b[0;32m    394\u001b[0m \u001b[38;5;66;03m# call optimizer step post hooks\u001b[39;00m\n",
      "File \u001b[1;32me:\\anaconda\\envs\\huggingface\\Lib\\site-packages\\torch\\optim\\optimizer.py:76\u001b[0m, in \u001b[0;36m_use_grad_for_differentiable.<locals>._use_grad\u001b[1;34m(self, *args, **kwargs)\u001b[0m\n\u001b[0;32m     74\u001b[0m     torch\u001b[38;5;241m.\u001b[39mset_grad_enabled(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdefaults[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdifferentiable\u001b[39m\u001b[38;5;124m'\u001b[39m])\n\u001b[0;32m     75\u001b[0m     torch\u001b[38;5;241m.\u001b[39m_dynamo\u001b[38;5;241m.\u001b[39mgraph_break()\n\u001b[1;32m---> 76\u001b[0m     ret \u001b[38;5;241m=\u001b[39m func(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;241m*\u001b[39margs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m     77\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m     78\u001b[0m     torch\u001b[38;5;241m.\u001b[39m_dynamo\u001b[38;5;241m.\u001b[39mgraph_break()\n",
      "File \u001b[1;32me:\\anaconda\\envs\\huggingface\\Lib\\site-packages\\torch\\optim\\adam.py:168\u001b[0m, in \u001b[0;36mAdam.step\u001b[1;34m(self, closure)\u001b[0m\n\u001b[0;32m    157\u001b[0m     beta1, beta2 \u001b[38;5;241m=\u001b[39m group[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mbetas\u001b[39m\u001b[38;5;124m'\u001b[39m]\n\u001b[0;32m    159\u001b[0m     has_complex \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_init_group(\n\u001b[0;32m    160\u001b[0m         group,\n\u001b[0;32m    161\u001b[0m         params_with_grad,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    165\u001b[0m         max_exp_avg_sqs,\n\u001b[0;32m    166\u001b[0m         state_steps)\n\u001b[1;32m--> 168\u001b[0m     adam(\n\u001b[0;32m    169\u001b[0m         params_with_grad,\n\u001b[0;32m    170\u001b[0m         grads,\n\u001b[0;32m    171\u001b[0m         exp_avgs,\n\u001b[0;32m    172\u001b[0m         exp_avg_sqs,\n\u001b[0;32m    173\u001b[0m         max_exp_avg_sqs,\n\u001b[0;32m    174\u001b[0m         state_steps,\n\u001b[0;32m    175\u001b[0m         amsgrad\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mamsgrad\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    176\u001b[0m         has_complex\u001b[38;5;241m=\u001b[39mhas_complex,\n\u001b[0;32m    177\u001b[0m         beta1\u001b[38;5;241m=\u001b[39mbeta1,\n\u001b[0;32m    178\u001b[0m         beta2\u001b[38;5;241m=\u001b[39mbeta2,\n\u001b[0;32m    179\u001b[0m         lr\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlr\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    180\u001b[0m         weight_decay\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mweight_decay\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    181\u001b[0m         eps\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124meps\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    182\u001b[0m         maximize\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mmaximize\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    183\u001b[0m         foreach\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mforeach\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    184\u001b[0m         capturable\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mcapturable\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    185\u001b[0m         differentiable\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mdifferentiable\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    186\u001b[0m         fused\u001b[38;5;241m=\u001b[39mgroup[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mfused\u001b[39m\u001b[38;5;124m'\u001b[39m],\n\u001b[0;32m    187\u001b[0m         grad_scale\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mgrad_scale\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m),\n\u001b[0;32m    188\u001b[0m         found_inf\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mgetattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfound_inf\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m),\n\u001b[0;32m    189\u001b[0m     )\n\u001b[0;32m    191\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m loss\n",
      "File \u001b[1;32me:\\anaconda\\envs\\huggingface\\Lib\\site-packages\\torch\\optim\\adam.py:318\u001b[0m, in \u001b[0;36madam\u001b[1;34m(params, grads, exp_avgs, exp_avg_sqs, max_exp_avg_sqs, state_steps, foreach, capturable, differentiable, fused, grad_scale, found_inf, has_complex, amsgrad, beta1, beta2, lr, weight_decay, eps, maximize)\u001b[0m\n\u001b[0;32m    315\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m    316\u001b[0m     func \u001b[38;5;241m=\u001b[39m _single_tensor_adam\n\u001b[1;32m--> 318\u001b[0m func(params,\n\u001b[0;32m    319\u001b[0m      grads,\n\u001b[0;32m    320\u001b[0m      exp_avgs,\n\u001b[0;32m    321\u001b[0m      exp_avg_sqs,\n\u001b[0;32m    322\u001b[0m      max_exp_avg_sqs,\n\u001b[0;32m    323\u001b[0m      state_steps,\n\u001b[0;32m    324\u001b[0m      amsgrad\u001b[38;5;241m=\u001b[39mamsgrad,\n\u001b[0;32m    325\u001b[0m      has_complex\u001b[38;5;241m=\u001b[39mhas_complex,\n\u001b[0;32m    326\u001b[0m      beta1\u001b[38;5;241m=\u001b[39mbeta1,\n\u001b[0;32m    327\u001b[0m      beta2\u001b[38;5;241m=\u001b[39mbeta2,\n\u001b[0;32m    328\u001b[0m      lr\u001b[38;5;241m=\u001b[39mlr,\n\u001b[0;32m    329\u001b[0m      weight_decay\u001b[38;5;241m=\u001b[39mweight_decay,\n\u001b[0;32m    330\u001b[0m      eps\u001b[38;5;241m=\u001b[39meps,\n\u001b[0;32m    331\u001b[0m      maximize\u001b[38;5;241m=\u001b[39mmaximize,\n\u001b[0;32m    332\u001b[0m      capturable\u001b[38;5;241m=\u001b[39mcapturable,\n\u001b[0;32m    333\u001b[0m      differentiable\u001b[38;5;241m=\u001b[39mdifferentiable,\n\u001b[0;32m    334\u001b[0m      grad_scale\u001b[38;5;241m=\u001b[39mgrad_scale,\n\u001b[0;32m    335\u001b[0m      found_inf\u001b[38;5;241m=\u001b[39mfound_inf)\n",
      "File \u001b[1;32me:\\anaconda\\envs\\huggingface\\Lib\\site-packages\\torch\\optim\\adam.py:522\u001b[0m, in \u001b[0;36m_multi_tensor_adam\u001b[1;34m(params, grads, exp_avgs, exp_avg_sqs, max_exp_avg_sqs, state_steps, grad_scale, found_inf, amsgrad, has_complex, beta1, beta2, lr, weight_decay, eps, maximize, capturable, differentiable)\u001b[0m\n\u001b[0;32m    519\u001b[0m         device_grads \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39m_foreach_add(device_grads, device_params, alpha\u001b[38;5;241m=\u001b[39mweight_decay)\n\u001b[0;32m    521\u001b[0m \u001b[38;5;66;03m# Decay the first and second moment running average coefficient\u001b[39;00m\n\u001b[1;32m--> 522\u001b[0m torch\u001b[38;5;241m.\u001b[39m_foreach_lerp_(device_exp_avgs, device_grads, \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m-\u001b[39m beta1)\n\u001b[0;32m    524\u001b[0m torch\u001b[38;5;241m.\u001b[39m_foreach_mul_(device_exp_avg_sqs, beta2)\n\u001b[0;32m    525\u001b[0m torch\u001b[38;5;241m.\u001b[39m_foreach_addcmul_(device_exp_avg_sqs, device_grads, device_grads, \u001b[38;5;241m1\u001b[39m \u001b[38;5;241m-\u001b[39m beta2)\n",
      "\u001b[1;31mKeyboardInterrupt\u001b[0m: "
     ]
    }
   ],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "from tqdm import tqdm\n",
    "from torch import nn,optim\n",
    "from Focal_loss import FocalLoss\n",
    "# 定义损失函数和优化器\n",
    "device = 'cuda'\n",
    "loss_func = FocalLoss(4)  \n",
    "optimizer = optim.Adam(model.parameters(), lr=0.0001)\n",
    "\n",
    "# 训练循环\n",
    "start = 0\n",
    "num_epochs = 10\n",
    "writer = SummaryWriter(log_dir='./BertLSTMlog')\n",
    "\n",
    "for epoch in range(start,num_epochs):\n",
    "    model.to(device)\n",
    "    model.train()\n",
    "    running_loss = 0.0\n",
    "    correct_predictions = 0\n",
    "    train_sum = 0\n",
    "    train_recall = 0\n",
    "    correct_recall = 0\n",
    "    val_sum = 0\n",
    "    for data in tqdm(train_loader):\n",
    "        inputs, mask, labels = data[0].to(device),data[1].to(device),data[2].to(device).flatten()\n",
    "\n",
    "        true_sample = labels!=-100\n",
    "        true_labels = labels[true_sample]   # 去除非样本元素\n",
    "\n",
    "        optimizer.zero_grad()\n",
    "        _,outputs = model(inputs,mask)\n",
    "        batch_out = outputs.reshape(-1,outputs.shape[-1])[true_sample]\n",
    "        loss = loss_func(batch_out, true_labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        running_loss += loss.item()\n",
    "        _, predictions = torch.max(batch_out, 1)\n",
    "        correct_predictions += (predictions == true_labels).sum().item()\n",
    "        train_sum+=len(true_labels)\n",
    "        train_recall+=true_labels.sum().item()\n",
    "        correct_recall += predictions[true_labels].sum().item()\n",
    "\n",
    "    epoch_loss = running_loss / train_sum\n",
    "    epoch_acc = correct_predictions / train_sum\n",
    "\n",
    "    model.eval()\n",
    "    val_running_loss = 0.0\n",
    "    val_correct_predictions = 0\n",
    "    val_correct_recall = 0\n",
    "    val_recall = 0\n",
    "    with torch.no_grad():\n",
    "        for data in val_loader:\n",
    "            inputs, mask, labels = data[0].to(device),data[1].to(device),data[2].to(device).flatten()\n",
    "\n",
    "            true_sample = labels!=-100\n",
    "            true_labels = labels[true_sample]\n",
    "\n",
    "            _,outputs = model(inputs,mask)\n",
    "            batch_out = outputs.reshape(-1,outputs.shape[-1])[true_sample]\n",
    "            loss = loss_func(batch_out,true_labels)\n",
    "\n",
    "            val_running_loss += loss.item()\n",
    "            _, predictions = torch.max(batch_out, 1)\n",
    "            val_correct_predictions += (predictions == true_labels).sum().item()\n",
    "            val_sum+=len(true_labels)\n",
    "            val_recall += true_labels.sum().item()\n",
    "            val_correct_recall += predictions[true_labels].sum().item()\n",
    "\n",
    "\n",
    "    val_epoch_loss = val_running_loss / val_sum\n",
    "    val_epoch_acc = val_correct_predictions / val_sum\n",
    "    # 用tensorboard可视化\n",
    "    writer.add_scalars('model',tag_scalar_dict={'train_acc':epoch_acc,\n",
    "                                                                      'loss':epoch_loss,\n",
    "                                                                      'val_acc':val_epoch_acc\n",
    "                                                                      },global_step=epoch+1)\n",
    "\n",
    "    print(f'Epoch {epoch+1}/{num_epochs}')\n",
    "    print(f'Train Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f} Recall: {correct_recall/train_recall}')\n",
    "    print(f'Val Loss: {val_epoch_loss:.4f} Acc: {val_epoch_acc:.4f} Recall: {val_correct_recall/val_recall}')\n",
    "\n",
    "writer.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "huggingface",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
